#define LOG_TAG "fileHandle"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <utils/Logger.h>
#include <pthread.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

#include "common/_define.h"

#include "utils/fileHandle.h"

namespace linuxSys{

static int32_t _delete_dir(const char* dirname) {

    int32_t ret = 0;
    char path[256] = {0};
    DIR * dir;
    struct dirent * ptr;
    dir = opendir(dirname);
    if (!dir) {
        log_err("opendir \"%s\" fail!\n", dirname);
        return FAILURE;
    }

    while((ptr = readdir(dir)) != NULL) {
        //log_debug("d_name : %s\n", ptr->d_name);
        //log_debug("d_type : %d\n", ptr->d_type);
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
            continue;

        if (ptr->d_type == 4) {
            memset(path,0x00,sizeof(path));
            strcpy(path,dirname);
            strcat(path,"/");
            strcat(path,ptr->d_name);
            log_debug("path dir:\"%s\"\n", path);
            ret = _delete_dir(path);
            if (ret != 0) {
                log_err("deletedir \"%s\" fail!\n", path);
            }
        } else {
            memset(path,0x00,sizeof(path));
            strcpy(path,dirname);
            strcat(path,"/");
            strcat(path,ptr->d_name);
            log_debug("path :\"%s\"\n", path);
            ret = remove(path);
            if (ret != 0) {
                log_err("remove \"%s\" fail! %s\n",ptr->d_name, strerror(errno));
            }
            log_debug("remove \"%s\"\n", path);
        }
    }
    closedir(dir);
    ret = remove(dirname);
    if (ret != 0) {
        log_err("remove \"%s\" fail! %s\n",ptr->d_name, strerror(errno));
    }
    return SUCCESS;
}

int32_t delete_dir(const std::string& dirname) {

    if (access(dirname.c_str(), F_OK) < 0) {
        log_err("[%s] dir does not exist!\n",dirname.c_str());
        return FAILURE;
    }
    return _delete_dir(dirname.c_str());
}

int32_t delete_file(const std::string& rel_path) {

    log_debug("[delete_file] delete file \"%s\"",rel_path.c_str());
    if (access(rel_path.c_str(), F_OK) < 0) {
        log_err("[%s] File does not exist!\n",rel_path.c_str());
        return FAILURE;
    }

    int32_t ret = remove(rel_path.c_str());
    if (-1 == ret) {
        log_err("delete [%s] failed!\n",rel_path.c_str());
        return FAILURE;
    }
    return SUCCESS;
}

int32_t get_file_length(const std::string& rel_path, int32_t &length) {

    log_err("[get_file_length] PATH:%s", rel_path.c_str());
    if (access(rel_path.c_str(), 0) != 0) {
        log_err("The file does not exist!\n");
        return FAILURE;
    } else {
        FILE* fp = fopen(rel_path.c_str(), "rb");
        if (_NULL == fp) 
        {
            log_err("open file error:");
            return FAILURE;
        }
	
        fseek(fp, 0, SEEK_END);
        length = (int32_t) ftell(fp);
        fclose(fp);
        return SUCCESS;
    }
}

int32_t get_file(const std::string& rel_path, char *data, int32_t& length) {

    log_err("[get_file] PATH:%s", rel_path.c_str());
    if (access(rel_path.c_str(), 0) != 0) {
        log_err("The file does not exist!\n");
        return FAILURE;
    }
    FILE* fp = fopen(rel_path.c_str(), "rb");
    if (_NULL == fp) {
        log_err("open file error:");
        return FAILURE;
    }

    fseek(fp, 0, SEEK_END);
    int32_t size = (int32_t) ftell(fp);
    fseek(fp, 0, SEEK_SET);

    if (size >= length) {
        int unused __attribute__((unused));
        unused = fread(data,1,length,fp);
    } else {
        int unused __attribute__((unused));
        unused = fread(data,1,size,fp);
        length = size;
    }
    fclose(fp);
    return SUCCESS;
}

int32_t save_file_without_sync(const std::string& rel_path, char *data, int32_t length) {

    char* path = strdup(rel_path.c_str());
    if (NULL == path) return FAILURE;
    char* dir_path = dirname(path);
    CreateDir(dir_path);
    free(path);

    if (NULL == data) {
        log_err("DTHDB file data is empty\n");
        return FAILURE;
    }
    FILE* fd = fopen(rel_path.c_str(), "wb");
    if (fd == _NULL) {
        log_err("Create DTHDB create save file (%s) failed!\n", rel_path.c_str());
        return FAILURE;
    }
    int unused __attribute__((unused));
    unused = fwrite(data, 1, length, fd);
    fclose(fd);
    return SUCCESS;
}

int32_t save_file(const std::string& rel_path, char *data, int32_t length) {

    int32_t ret = save_file_without_sync(rel_path, data, length);
    sync();
    return ret;
} 

int32_t IsDir(const std::string& path) {  

    struct stat buf;  
    if (stat(path.c_str(), &buf)==-1) {  
        log_err("stat :%s",strerror(errno));  
        log_err("path = %s\n", path.c_str());  
        return -1;  
    }  
    return S_ISDIR(buf.st_mode);  
} 


int32_t CreateDir(const std::string& path) {  
    char pathname[256] = {0}; 

    strcpy(pathname, path.c_str());  
    int32_t len=strlen(pathname);  
    if (pathname[len-1]!='/') {  
        strcat(pathname, "/");  
        len++;  
    }  
    for (int32_t i=0; i<len; i++) {  
        if (pathname[i]=='/') {  
            if (i == 0)
                continue;
            pathname[i]=0;  
            if (access(pathname, F_OK)) {//判断路径是否存在  
                //不存在则创建  
                if (mkdir(pathname, 0755)==-1) {  
                    log_debug("i:%d mkdir \"%s\":%s\n",i,pathname,strerror(errno));  
                    return -1;  
                }  
            }  
            pathname[i]='/';  
        }  
    }  
    return 0;  
}
 
int32_t FileCopy(const std::string& srcpath, const std::string& dstpath) {
    #define BUFFER_SIZE 1024 
    int32_t srcfd, dstfd, file_len, ret=1;  
    char buffer[BUFFER_SIZE];  
    char dstfn[256] = {0};  
    if (access(srcpath.c_str(), R_OK)) {  
        log_debug("Cannot copy does not exist or unreadable files: %s\n", srcpath.c_str());  
        return -1;  
    }  
    strcpy(dstfn, dstpath.c_str());  
    //如果@dstpath存在且是目录则在其后加上srcpath的文件名  
    if (access(dstpath.c_str(), F_OK)==0 && IsDir(dstpath)==1) {  
        if (dstfn[strlen(dstfn)-1]!='/') {  
            strcat(dstfn, "/");  
        }  
        if (strchr(srcpath.c_str(), '/')) {  
            strcat(dstfn, strrchr(srcpath.c_str(), '/'));  
        } else {  
            strcat(dstfn, srcpath.c_str());  
        }  
    }
    srcfd=open(srcpath.c_str(), O_RDONLY);  
    struct stat stabuff;
    if (0 == fstat(srcfd,&stabuff)) {
        dstfd=open(dstfn, O_WRONLY|O_CREAT,stabuff.st_mode); 
    } else {
        dstfd=open(dstfn, O_WRONLY|O_CREAT,0755);  
    }    
    if (srcfd == -1 || dstfd == -1) {  
        if (srcfd!=-1) {  
            close(srcfd);  
        }  
        log_debug("open \"%s\":%s!\n",dstfn,strerror(errno));  
        return -1;  
    }
      
    file_len= lseek(srcfd, 0L, SEEK_END);  
    lseek(srcfd, 0L, SEEK_SET);  
    while(ret) {  
        ret= read(srcfd, buffer, BUFFER_SIZE);  
        if (ret==-1) {  
            log_debug("read fail!:%s\n",strerror(errno));  
            close(srcfd);  
            close(dstfd);  
            return -1;  
        }  
        int unused __attribute__((unused));
        unused = write(dstfd, buffer, ret);  
        file_len-=ret;  
        bzero(buffer, BUFFER_SIZE);  
    }  
    close(srcfd);  
    close(dstfd);  
    if (ret) {  
        log_debug("文件: %s, 没有拷贝完!\n", srcpath.c_str());  
        return -1;  
    }  
    return 0;  
}

/* 
 * 目录拷贝 
 * */  
int32_t DirCopy(const std::string& srcpath, const std::string& dstpath)  
{  
    int32_t ret;  
    if (!(ret=IsDir(srcpath))) {//如果@srcpath 是文件，直接进行文件拷贝  
        FileCopy(srcpath, dstpath);  
        return 0;  
    } else if(ret!=1) {  //目录或文件不存在  
        return -1;  
    }  
    DIR* dir = opendir(srcpath.c_str());  
    CreateDir(dstpath);  
    struct dirent* ptr;
    while((ptr=readdir(dir))!=NULL) {  
        char frompath[256] = {0};
        char topath[256] = {0};
        bzero(frompath, 256);  
        bzero(topath, 256);  
        strcpy(frompath, srcpath.c_str());  
        strcpy(topath, dstpath.c_str());  
        if (frompath[strlen(frompath)-1]!='/') {  
            strcat(frompath, "/");  
        }  
        if (topath[strlen(topath)-1]!='/') {  
            strcat(topath, "/");  
        }  
        strcat(frompath, ptr->d_name);  
        //log_debug("%s\n", frompath);  
        strcat(topath, ptr->d_name);  
        if ((ret=IsDir(frompath))==1) {  
            if (strcmp(strrchr(frompath, '/'), "/.")==0  
                || strcmp(strrchr(frompath, '/'), "/..")==0) {  
                //log_debug(". or ..目录不用复制\n");  
            } else {  
                DirCopy(frompath, topath);  
            }  
        } else if (ret!=-1) {  
            FileCopy(frompath, topath);  
        }  
    }  
    closedir(dir);  
    return 0;  
}  

int32_t compareFileAmendTime(const std::string& oldFile ,const std::string& newFile)
{
    struct stat stabuff;

    if(0 != stat(oldFile.c_str(),&stabuff)) {
        log_err("[compareFileAmendTime] stat \"%s\" fail!%s\n",oldFile.c_str(),strerror(errno));
        return -1;
    }
    struct timespec oldFileTime = stabuff.st_mtim;
    if(0 != stat(newFile.c_str(),&stabuff)) {
        log_err("[compareFileAmendTime] stat \"%s\" fail!%s\n",oldFile.c_str(),strerror(errno));
        return -1;
    }
    struct timespec newFileTime = stabuff.st_mtim;
    log_debug("[compareFileAmendTime] \"%s\" sec %lu nsec %lu\n",oldFile.c_str(),oldFileTime.tv_sec,oldFileTime.tv_nsec);
    log_debug("[compareFileAmendTime] \"%s\" sec %lu nsec %lu\n",newFile.c_str(),newFileTime.tv_sec,newFileTime.tv_nsec);
    if (oldFileTime.tv_sec < newFileTime.tv_sec) {
        return 0;
    } else if (oldFileTime.tv_sec == newFileTime.tv_sec) {
        if (oldFileTime.tv_nsec < newFileTime.tv_sec) {
            return 0;
        }
    }
    return 1;
}

int32_t compareFileSize(const std::string& bigSizeFile ,const std::string& smallSizeFile) {
    
    struct stat stabuff;

    if(0 != stat(bigSizeFile.c_str(),&stabuff)) {
        log_debug("[compareFileSize] stat \"%s\" fail!%s\n",bigSizeFile.c_str(),strerror(errno));
        return -1;
    }
    off_t bigFileSize = stabuff.st_size;
    if(0 != stat(smallSizeFile.c_str(),&stabuff)) {
        log_debug("[compareFileSize] stat \"%s\" fail!%s\n",smallSizeFile.c_str(),strerror(errno));
        return -1;
    }
    off_t smallFileSize = stabuff.st_size;

    log_debug("[compareFileSize] \"%s\" off_t %lu\n",bigSizeFile.c_str(),bigFileSize);
    log_debug("[compareFileSize] \"%s\" off_t %lu\n",smallSizeFile.c_str(),smallFileSize);
    if (bigFileSize >= smallFileSize) {
        return 0;
    }
    return 1;
}

};